#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <assert.h>

#include "fonctionsTCP.h"

#include "client.h"

int main( int argc, char** argv ){
	int socket_arbitre, socket_ia;
	int port_arbitre, port_ia;
	int id_joueur;
	int id_adversaire;
	int finTournoi;
	int premier;
	int numCoup;
	int partiContinue;
	int err;
	char* nom_arbitre, * nom_ia;
	char* nom_joueur = (char*) NULL;
	TypPiece couleur;
	TypValCoup validite;
	TypPosition depart;
	TypPosition arrivee;
	
	TypPropCoup typeCoup_moi = DEPL;
	TypBooleen bloque_moi = FAUX;
	
	TypPropCoup typeCoup_adv;
	TypBooleen bloque_adv;
	
	depart.axeLettre = AXE_A;
	depart.axeChiffre = AXE_UN;
	
	arrivee.axeLettre = AXE_D;
	arrivee.axeChiffre = AXE_QUATRE;
	
	if( argc != 6 ){
		printf("USAGE:\t./client ADDRESS_ARBITRE PORT_ARBITRE ADDRESS_IA PORT_IA NOM_JOUEUR\n\n");
		printf("ADDRESS_ARBITRE:\tl'adresse du serveur de l'arbitre\nPORT_ARBITRE:\t\tle numero du port du serveur arbitre\n");
		printf("ADDRESS_IA:\t\tl'adresse du serveur de l'IA\nPORT_IA:\t\tle numero du port du serveur de l'IA\n");
		printf("NOM_JOUEUR:\t\tle nom du joueur\n");
		return -6;
	}
	
	nom_arbitre = argv[1];
	port_arbitre = atoi( argv[2] );
	nom_ia = argv[3];
	port_ia = atoi(argv[4]);
	nom_joueur = argv[5];

	
	// Connexion à l'IA puis à l'arbitre
	socket_ia = socketClient(nom_ia, port_ia);
	if( socket_ia < 0 ){
		printf("Erreur! La socket avec l'IA n'a pas pu etre crée!\n");
	}
	
	socket_arbitre = socketClient(nom_arbitre, port_arbitre);
	if( socket_arbitre < 0 ){
		printf("Erreur! La socket arbitre n'a pas pu etre crée!\n");
		return -1;
	}
	
	// Identification auprès de l'arbitre
	err = envoi_identification( socket_arbitre, nom_joueur);
	if( err < 0 ){
		printf("Erreur! l'envoi de l'identification n'a pas pu etre realise\n");
		return -2;
	}
	
	err = retour_identification(socket_arbitre, &id_joueur);		
	if( err != ERR_OK ){
		printf("Erreur! le retour de l'identification donne une erreur.\n");
		return -3;
	}
	
	// Demande de partie auprès de l'arbitre
	err = envoi_partie( socket_arbitre, id_joueur );
	if( err < 0 ){
		printf("Erreur! l'envoi de l'erreur ne s'est pas deroule correctement.\n");
		return -4;
	}
	
	err = retour_partie( socket_arbitre, &id_adversaire, &finTournoi, &premier);
	if( err != ERR_OK ){
		printf("Erreur! le retour de la demande de partie comporte une erreur.\n");
		return -5;
	}
	
	// Détermination de la couleur du joueur pour tout le tournoi
	if (premier) {
		couleur = ROUGE;
	} else {
		couleur = BLEU;
	}
	
	sendInt(socket_ia, premier);
	
	while (!finTournoi) {
	printf("\nDebut de partie.\n");
		typeCoup_moi = DEPL;
		partiContinue = 1;
		numCoup = 0;
		
		do {
			printf("Tour n°%d\n", numCoup);
			printf("Joueur %d, couleur %d\n", premier, couleur);
			if (premier) {
				/*
				 * ---------------------------- Premier joueur ----------------------------
				 */
				// Attente d'un coup depuis l'ia
				err = reception_coup_ia(socket_ia, &typeCoup_moi, &bloque_moi, &depart, &arrivee);
				
				// Envoi du coup à l'arbitre
				envoi_coup(socket_arbitre, couleur, typeCoup_moi, bloque_moi, depart, arrivee, numCoup);
				retour_coup(socket_arbitre, &validite);
				
				if (validite != VALID || typeCoup_moi == GAGNE || typeCoup_moi == PERD || typeCoup_moi == NULLE) {
					// Le coup envoyé n'est pas valide, ou la partie est terminée
					partiContinue = 0;
					envoi_partieContinue(socket_ia, partiContinue);
				} else {
					// Le coup envoyé est valide et la partie continue
					// Reception du coup de l'adversaire
					retour_coup(socket_arbitre, &validite);
					if (validite != VALID) {
						partiContinue = 0;
						envoi_partieContinue(socket_ia, partiContinue);
					} else {
						reception_requete_coup(socket_arbitre, &typeCoup_adv, &bloque_adv, &depart, &arrivee);
						if (typeCoup_adv == GAGNE || typeCoup_adv == PERD || typeCoup_adv == NULLE) {
							partiContinue = 0;
							envoi_partieContinue(socket_ia, partiContinue);
						} else {
							envoi_partieContinue(socket_ia, partiContinue);
							// Envoi du coup de l'adversaire à l'ia
							envoi_coup_ia(socket_ia, depart, arrivee);
						}
					}
				}
				
			} else {
				/*
				 * --------------------------- Second Joueur ----------------------------
				 */
				// Reception du coup de l'adversaire
					retour_coup(socket_arbitre, &validite);
					if (validite != VALID) {
						partiContinue = 0;
						envoi_partieContinue(socket_ia, partiContinue);
					} else {
						reception_requete_coup( socket_arbitre, &typeCoup_adv, &bloque_adv, &depart, &arrivee);
					
						if (typeCoup_adv == GAGNE || typeCoup_adv == PERD || typeCoup_adv == NULLE) {
							partiContinue = 0;
							envoi_partieContinue(socket_ia, partiContinue);

						} else {
							envoi_partieContinue(socket_ia, partiContinue);
						
							// envoi coup de l'adversaire a l'ia
							envoi_coup_ia(socket_ia, depart, arrivee);
						
							// Attente d'un coup depuis l'ia
							reception_coup_ia(socket_ia, &typeCoup_moi, &bloque_moi, &depart, &arrivee);
							
							// Envoi du coup à l'arbitre
							envoi_coup(socket_arbitre, couleur, typeCoup_moi, bloque_moi, depart, arrivee, numCoup);
							retour_coup( socket_arbitre, &validite);
						
							if (validite != VALID || typeCoup_moi == GAGNE || typeCoup_moi == PERD || typeCoup_moi == NULLE) {
								partiContinue = 0;
							}
							envoi_partieContinue(socket_ia, partiContinue);
						}
					}
				
			}
			
			numCoup++;
		
		} while(partiContinue);
		printf("Fin de la partie\n");
		printf("Je suis: %d\n", id_joueur);
		envoi_partie( socket_arbitre, id_joueur );
		retour_partie( socket_arbitre, &id_adversaire, &finTournoi, &premier);
		envoi_finTournoi(socket_ia, finTournoi);
		if (!finTournoi) {
			sendInt(socket_ia, premier);
		}
	}

	shutdown( socket_arbitre, 2);
	close( socket_arbitre );
	
	return 0;
}

/*
 * Fonctions de communication avec l'arbitre
 */
int envoi_identification( int socket_arbitre, char* nom_joueur ){
	TypIdentificationReq requete_ident;
	requete_ident.idRequest = IDENTIFICATION;
	strcpy(requete_ident.nom, nom_joueur );
	return send(socket_arbitre, &requete_ident, sizeof( TypIdentificationReq ), 0);
}

int retour_identification( int socket_arbitre, int* id_joueur ){
	TypIdentificationRep reponse_ident;
	recv(socket_arbitre, &reponse_ident, sizeof( TypIdentificationRep ), 0);
	*id_joueur = reponse_ident.joueur;
	return reponse_ident.err;
}

int envoi_partie( int socket_arbitre, int id_joueur ){
	TypPartieReq requete;
	requete.joueur = id_joueur;
	requete.idRequest = PARTIE;
	return send( socket_arbitre, &requete, sizeof( TypPartieReq ), 0);
}

int retour_partie( int socket_arbitre, int* adversaire, int* finTournoi, int* premier ){
	TypPartieRep reponse;
	recv( socket_arbitre, &reponse, sizeof( TypPartieRep ), 0);
	*adversaire = reponse.adversaire;
	*finTournoi = reponse.finTournoi;
	*premier = reponse.premier;
	return reponse.err;
}

int envoi_coup( int socket_arbitre, TypPiece piece, TypPropCoup typeCoup, TypBooleen bloque, TypPosition depart,
								TypPosition arrivee, int numCoup){
	TypCoupReq requete;
	requete.idRequest = COUP;
	requete.couleurPiece = piece;
	requete.propCoup = typeCoup;
	requete.bloq = bloque;
	requete.caseDepPiece = depart;
	requete.caseArrPiece = arrivee;
	requete.numeroDuCoup = numCoup;
	

	return send(socket_arbitre, &requete, sizeof( TypCoupReq ), 0);
}

int retour_coup( int socket_arbitre, TypValCoup* isValide){
	TypCoupRep reponse;
	recv(socket_arbitre, &reponse, sizeof( TypCoupRep ), 0);
	*isValide = reponse.validCoup;

	return reponse.err;
}

int reception_requete_coup( int socket_arbitre, TypPropCoup* typeCoup, TypBooleen* bloque, TypPosition* depart, TypPosition* arrivee){
	TypCoupReq requete;
	int err = recv( socket_arbitre, &requete, sizeof( TypCoupReq), 0);
	*typeCoup = requete.propCoup;
	*bloque = requete.bloq;
	*depart = requete.caseDepPiece;
	*arrivee = requete.caseArrPiece;

	return err;
}


/*
 * Fonctions de communication avec l'ia
 */
int reception_coup_ia(int socket_ia, TypPropCoup* typeCoup_moi, TypBooleen* bloque_moi, TypPosition* depart, TypPosition* arrivee) {
	*typeCoup_moi = recvInt(socket_ia);
	
	*bloque_moi = recvInt(socket_ia);
		
	depart->axeLettre = recvInt(socket_ia); 
	depart->axeChiffre = recvInt(socket_ia); 
		
	arrivee->axeLettre = recvInt(socket_ia); 
	arrivee->axeChiffre = recvInt(socket_ia); 
	
	return 0;
}

int envoi_coup_ia(int socket_ia, TypPosition depart, TypPosition arrivee) {	
	sendInt(socket_ia, (int) depart.axeLettre);
	sendInt(socket_ia, (int) depart.axeChiffre);
	
	sendInt(socket_ia, (int) arrivee.axeLettre);
	sendInt(socket_ia, (int) arrivee.axeChiffre);
	
	return 0;
}

void envoi_partieContinue(int socket_ia, int partieContinue) {
	sendInt(socket_ia, partieContinue);
}

void envoi_finTournoi(int socket_ia, int finTournoi) {
	sendInt(socket_ia, finTournoi);
}


/*
 * Fonctions de reception/envoi 
 */
static void secure_recv(int sock, void *buf, size_t size) {
  size_t received = 0;
  char *data = buf;
  do {
    ssize_t ret = recv(sock, data + received, size - received, 0);
    if (ret == -1) {
      perror("recv");
    }
    received += ret;
  } while (received != size);
}

int recvInt(int sock) {
  int data;
  secure_recv(sock, &data, sizeof(int));
  return ntohl(data);
}

static void secure_send(int sock, const void *buf, size_t size) {
  size_t sent = 0;
  const char *data = buf;
  do {
    ssize_t ret = send(sock, data + sent, size - sent, 0);
    if (ret == -1) {
      perror("send");
    }
    sent += ret;
  } while (sent != size);
}

void sendInt(int sock, int i) {
  int data = htonl(i);
  secure_send(sock, &data, sizeof(int));
}
